/*
 * 配置 json
 * 1：obj
 * 2：binds:[{key:,el:,type,event}]
 */
export default class xBind {

    obj;
    // key的订阅者
    subscribePool = new Map();

    //
    reform() {
        let _this = this;
        Object.keys(this.obj).forEach((key) => _this.defineObserver.bind(_this)(key, this.obj[key]));
    }

    defineObserver(key, val) {
        let xks = new xSubscribe();
        this.subscribePool.set(key,xks);
        Object.defineProperty(this.obj, key, {
            enumerable: true,
            configurable: true,
            get: function() {
                return val;
            },
            // 对于 xComp 组件 值相等也需要 通知下。
            set: function(newVal) {
                /*if(newVal == val && (typeof  newVal !='object')) {
                    return;
                } */
                let oldVal = val;
                val = newVal;
                xks.notify(oldVal,newVal);
            }
        });
    }

	addWatcher(binds){
        let _this = this;
        this.subscribePool.forEach(function(subscribe,key) {
			let tmpSubs = binds.filter((bind) => bind.key == key);
			// 增加 watcher ,绑定开始 
			tmpSubs.forEach((bind) => {
				let watcher = new xWatcher({obj:_this.obj, key:bind.key,
                    el:bind.el, type:bind.type,event:bind.event,
                    subscribe});
                subscribe.addWatcher(watcher) ;
				//同步 第一次  注意：click 不需要同步
                if(bind.type!="click")
                    watcher.update();
			});
		})
	};
	
	constructor(cfg) {
	    this.obj = cfg.obj;
		this.reform();
		this.addWatcher( cfg.binds);
	}

}

// key的订阅者
// 数组 里面的项 是 watcher 
class xSubscribe {

    watchers = [];

	addWatcher(watcher) {
		this.watchers.push(watcher);
	}
	
	notify(oldVal,newVal) {
		this.watchers.forEach(function(watcher) {
		    if(watcher.vCfg.type =='xComp')
                watcher.update();
            else{
                if(oldVal === newVal && (typeof  newVal !='object')) {
                    return;
                }else
                    watcher.update();
            }

		});
	}
}

//用于更新控件 的数据  和 侦听数据的变化
class xWatcher{
    /*
    *  subscribe ,obj,key,type,event,el
    * */
    vCfg;

    //更新控件
	updater = {
		'text': function(el, value) {
            document.getElementById(el).textContent = typeof value == 'undefined' ? '' : value;
		},
		'html': function(el, value) {
            document.getElementById(el).innerHTML = typeof value == 'undefined' ? '' : value;
		},

		'class': function(el, value) {
            document.getElementById(el).className = typeof value == 'undefined' ? '' : value;
		},

		'value': function(el, value) {
            document.getElementById(el).value = typeof value == 'undefined' ? '' : value;
		},

        'select': function(el, value) {
            document.getElementById(el).value = typeof value == 'undefined' ? '' : value;
        },

		'checkbox-group': function(el, value) {
			document.getElementsByName(el).forEach(node =>{
                    if(value.indexOf(node.value)>-1) 
                        node.checked = true;
                    else
                        node.checked =false;
                });
		},
		
		'radio-group': function(el, value) {
            document.getElementsByName(el).forEach(node =>{
                if(value==node.value)
                    node.checked = true;
                else
                    node.checked =false;
            });
		},

        'attr': function(el,value,type) {
            let tmpValue = typeof value == 'undefined' ? '' : value;
            let xType = type.replace('attr-','');
            if(document.getElementById(el)){
                if(xType =="disabled" && tmpValue == false)
                    document.getElementById(el).removeAttribute(xType);
                else
                    document.getElementById(el).setAttribute(xType,tmpValue);
            }
        },

        'style': function(el,value,type) {
            let tmpValue = typeof value == 'undefined' ? '' : value;
            let xType = type.replace('style-','');
            document.getElementById(el).style[xType] = tmpValue;
        },

        'xComp':function(el,value){
            el.setValue(value);
        },
        'click':function(){
        }
    } 

    //侦听数据的变化
	constructor(cfg) {
	    this.vCfg = cfg;
	    /*
        this.key = key;
		this.obj = obj;
		this.type = type;
		this.el = el;
		this.event=event;
	    * */

		switch (cfg.type){
            case 'value':
            case 'attr-checked':
            case 'text':
                this.addEvOfInput();
                break;
            case 'select':
                this.addEvOfSelect();
                break;
            case 'radio-group':
                this.addEvOfRadioGroup();
                break;
            case 'checkbox-group':
                this.addEvOfCheckboxGroup();
                break;
            case 'xComp':
                this.vCfg.el.setBindEvent(this.eventOfXComp.bind(this));
                break;
            case 'click':
                this.addEvOfClick();
            default:
        }
	}

	eventOfXComp(value){
	    //需要增加临时失效,防止重复设置
	    this.vCfg.obj[this.vCfg.key] = value;
    };

	update() {
	    //let xType = this.type.indexOf('-')>3?this.type.substring(0,this.type.indexOf('-')):this.type;
        let xType = this.vCfg.type.split('-')[0];
	    switch (xType){
            case 'attr':
            case 'data':
                this.updater['attr'](this.vCfg.el,this.vCfg.obj[this.vCfg.key],this.vCfg.type);
                break;
            case 'style':
                this.updater['style'](this.vCfg.el,this.vCfg.obj[this.vCfg.key],this.vCfg.type);
                break;
            case 'event':
                break;
            default:
                this.updater[this.vCfg.type](this.vCfg.el,this.vCfg.obj[this.vCfg.key]);
        }
	    
	    if(this.vCfg.event) this.vCfg.event(this.vCfg.obj[this.vCfg.key]);
	}

	addEvOfInput(){
	    let _this = this;
        document.getElementById(this.vCfg.el).addEventListener('input', function(e) {
            let newValue = e.target.value;
            _this.vCfg.subscribe.watchers.forEach(watcher =>
                watcher.vCfg.obj[watcher.vCfg.key] = newValue);
        })
    }

    addEvOfClick(){
        let _this = this;
        document.getElementById(this.vCfg.el).addEventListener('click', function(e) {
            _this.vCfg.subscribe.watchers.forEach(watcher =>
                watcher.vCfg.obj[watcher.vCfg.key] =
                    watcher.vCfg.obj[watcher.vCfg.key]?watcher.vCfg.obj[watcher.vCfg.key]+1:1);
        })

    }

    addEvOfSelect(){
        let _this = this;
        document.getElementById(this.vCfg.el).addEventListener('change', function(e) {
            let newValue = e.target.value;
            _this.vCfg.subscribe.watchers.forEach(watcher =>
                watcher.vCfg.obj[watcher.vCfg.key] = newValue);

        });
    }

    addEvOfRadioGroup(){
        let _this = this;
        document.getElementsByName(this.el).forEach(el =>{
            el.addEventListener('input', function(e) {
                let newValue = e.target.value;
                let checked  = e.target.checked;
                _this.vCfg.subscribe.watchers.forEach(watcher =>{
                        if(watcher.vCfg.el==e.target.name && checked){
                            watcher.vCfg.obj[watcher.vCfg.key]= newValue;
                        }
                });

            })

        });
    }

    addEvOfCheckboxGroup(){
        let _this = this;
        document.getElementsByName(this.vCfg.el).forEach(el =>{
            el.addEventListener('input', function(e) {
                let newValue = e.target.value;
                let checked  = e.target.checked;
                _this.vCfg.subscribe.watchers.forEach(watcher =>{
                    if(watcher.vCfg.el==e.target.name){
                        //array
                        if (checked)
                            watcher.vCfg.obj[watcher.vCfg.key].push(newValue);
                        else{
                            watcher.vCfg.obj[watcher.vCfg.key].forEach((item, index, arr) =>{
                                if(item == newValue) {
                                    arr.splice(index, 1);
                                }
                            });
                        }
                        watcher.vCfg.obj[watcher.vCfg.key]=watcher.vCfg.obj[watcher.vCfg.key];
                    }
                });

            })

        });
    }

}


